package org.example.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;
import sun.nio.ch.FileChannelImpl;

import java.io.*;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.text.DecimalFormat;

/**
 * @ProjectName: netdisk
 * @Desc: 文件相关工具
 * @Version: 1.0
 */
public class FileUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(FileUtil.class);

    public static byte[] file2Bytes(File file) {
        byte[] buffer = null;
        try (
                InputStream in = new FileInputStream(file);
                ByteArrayOutputStream out = new ByteArrayOutputStream()
        ) {

            byte[] bytes = new byte[1024];
            int i;
            while ((i = in.read(bytes)) != -1) {
                out.write(bytes, 0, i);
            }
            buffer = out.toByteArray();
        } catch (IOException e) {
            LOGGER.error(e.getMessage(), e);
            return new byte[0];
        }
        return buffer;
    }


    /**
     * 转换文件大小
     *
     * @param fileS
     * @return
     */
    public static String formatFileSize(long fileS) {
        DecimalFormat df = new DecimalFormat("#.00");
        String fileSizeString = "";
        String wrongSize = "0B";
        if (fileS == 0) {
            return wrongSize;
        }
        if (fileS < 1024) {
            fileSizeString = df.format((double) fileS) + "B";
        } else if (fileS < 1048576) {
            fileSizeString = df.format((double) fileS / 1024) + "KB";
        } else if (fileS < 1073741824) {
            fileSizeString = df.format((double) fileS / 1048576) + "MB";
        } else {
            fileSizeString = df.format((double) fileS / 1073741824) + "GB";
        }
        return fileSizeString;
    }

    //文件分割
    //文件路径,分割大小，输出目录
    public static void slicer(String path, int size, String dir) throws IOException {
        byte[] bytes = new byte[size];
        File file = new File(path);
        if (!file.exists())
            System.out.println("文件不存在");
        File f = new File(dir);
        if (!f.exists())
            f.mkdir();
        InputStream input = new FileInputStream(file);
        slicer(input, bytes, dir);
    }

    public static Integer slicer(InputStream input, byte[] bytes, String dir) throws IOException {
        int i = 0;
        int len;
        while ((len = input.read(bytes)) != -1) {
            File fi = new File(dir + File.separator + "chunk" + i);
            i++;
            fi.createNewFile();
            OutputStream out = new FileOutputStream(fi);
            out.write(bytes, 0, len);
            out.close();
        }
        input.close();
        return i;
    }

    public static Integer slicer(MultipartFile multipartFile, int size, String dir) throws IOException {
        byte[] bytes = new byte[size];
        InputStream inputStream = multipartFile.getInputStream();
        return slicer(inputStream, bytes, dir);
    }

    //清除目录
    public static void delDir(File dir) {
        if (dir.isDirectory()) {
            File[] files = dir.listFiles();
            if (null == files || files.length == 0) {
                dir.delete();
                return;
            }
            for (int i = 0; i < files.length; i++) {
                if (files[i].isDirectory())
                    delDir(files[i]);
                else {
                    files[i].delete();
                }
            }
            dir.delete();
        }
    }

    /**
     * 文件转移
     *
     * @param src    文件源路径
     * @param target
     */
    public static void transTo(String src, String target) {


    }

    public static void copyFile(String src, String dist) {
        long start = System.currentTimeMillis();
        copyFile(new File(src), new File(dist));
        LOGGER.info("普通文件流耗时:{}s", (System.currentTimeMillis() - start) / 1000.0);
    }

    public static void copyFile(File src, File dist) {
        try (InputStream in = new FileInputStream(src);
             OutputStream out = new FileOutputStream(dist);) {
            byte[] buffer = new byte[8192];
            int len = -1;
            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void copyFileBuffer(String src, String target) {//带有缓存字符流，可制定缓存大小
        long start = System.currentTimeMillis();
        try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(src));
             BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(target))) {
//            String line;//reader.readLine();
            int len;
            byte[] buffer = new byte[8192];
            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        LOGGER.info("buffer文件流耗时:{}s", (System.currentTimeMillis() - start) / 1000.0);
    }

    //非直接缓冲区,将缓冲区建立在 JVM 的内存之中。
    //没什么用
    public static void fileCopyNIO(String source, String target) {//使用NIO实现文件转移
        long start = System.currentTimeMillis();
        try (FileInputStream in = new FileInputStream(source);
             FileOutputStream out = new FileOutputStream(target);
             FileChannel inChannel = in.getChannel();
             FileChannel outChannel = out.getChannel()
        ) {
            ByteBuffer buffer = ByteBuffer.allocate(8192);//分配8192個字節緩存空間
            while (inChannel.read(buffer) != -1) {
                //当我们需要读取我们刚才写入缓冲区的数据时，首先需要做的是调用缓冲区的flip() 方法，将缓冲区切换到读模式
                buffer.flip();// 切换成读数据模式
                outChannel.write(buffer);// 将缓冲区中的数据写入通道
                buffer.clear();//// 清空缓冲区
            }
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        LOGGER.info("非直接缓冲区转移耗时:{}s", (System.currentTimeMillis() - start) / 1000.0);
    }
    public static void fileCopyNIO2(String source, String target) {//使用NIO实现文件转移
        long start = System.currentTimeMillis();
        try (FileChannel inChannel = FileChannel.open(Paths.get(source), StandardOpenOption.READ);
             FileChannel outChannel = FileChannel.open(Paths.get(target), StandardOpenOption.WRITE,
                     StandardOpenOption.READ, StandardOpenOption.CREATE)
        ) {
            long size = inChannel.size();
            for (long left = size; left > 0; ) {
                left -= inChannel.transferTo((size - left), left, outChannel);
            }
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        LOGGER.info("零拷贝转移耗时:{}s", (System.currentTimeMillis() - start) / 1000.0);
    }
    //通过 allocateDirect() 方法分配缓冲区，将缓冲区建立在物理内存之中。(内存映射文件)
    //内存映射文件能让你创建和修改那些因为太大而无法放入内存的文件。有了内存映射文件，你就可以认为文件已经全部读进了内存，然后把它当成一个非常大的数组来访问
    //已知严重内存消耗，并且不释放资源
    public static void fileCopyMemoryMapping(String source, String target) {
        long start = System.currentTimeMillis();
        /**
         * 使用 open 方法来获取通道
         * 需要两个参数
         * 参数1：Path 是 JDK1.7 以后给我们提供的一个类，代表文件路径
         * 参数2：Option  就是针对这个文件想要做什么样的操作
         *      --StandardOpenOption.READ ：读模式
         *      --StandardOpenOption.WRITE ：写模式
         *      --StandardOpenOption.CREATE ：如果文件不存在就创建，存在就覆盖
         */
        try (FileChannel inChannel = FileChannel.open(Paths.get(source), StandardOpenOption.READ);
             FileChannel outChannel = FileChannel.open(Paths.get(target), StandardOpenOption.WRITE,
                     StandardOpenOption.READ, StandardOpenOption.CREATE)) {
            long c;
            long mark=0;
            long limit=1024*1024*1024;//1G
            while(mark<inChannel.size()) {
                c=inChannel.size()-mark;
                if(c>limit)
                    c=limit;
                MappedByteBuffer inMapped = inChannel.map(FileChannel.MapMode.READ_ONLY, mark,c);
                MappedByteBuffer outMapped = outChannel.map(FileChannel.MapMode.READ_WRITE, mark,c);
                mark+=c;
                // 直接对缓冲区进行数据的读写操作
                byte[] dst = new byte[inMapped.limit()];
                inMapped.get(dst);  // 把数据读取到 dst 这个字节数组中去
                outMapped.put(dst); // 把字节数组中的数据写出去
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        LOGGER.info("内存映射文件转移耗时:{}s", (System.currentTimeMillis() - start) / 1000.0);
    }

    public static void main(String[] args) throws IOException {
//        String size = formatFileSize(100039);
//        System.out.println(size);
        File file=new File("C:\\Users\\wangcj\\Desktop\\tmp\\GTA5作弊.txt");
        System.out.println("AbsoluteFile"+file.getAbsoluteFile());
        System.out.println("Path"+file.getPath());
        System.out.println("CanonicalFile"+file.getCanonicalFile());
        System.out.println("FreeSpace"+file.getFreeSpace());
        System.out.println("TotalSpace"+file.getTotalSpace());
    }
}
